package model

import (
    "errors"
    "git.inspur.com/x/models"
)

type PortBinding struct {
    Id          string `xorm:"pk"`
    PortId      string `json:"portId"`
    FixedIp     string `json:"ipAddr"`
    PortMac     string `json:"macAddr"`
    PortName    string
    VpcId       string `json:"vpcId"`
    VpcCidr     string `json:"cidr"`
    SubnetId    string `json:"subnetId"`
    SubnetGwIp  string `json:"gateway"`
    ServerId    string `json:"serverId"`
    FunctionNum int
    CreateTime  models.Time `xorm:"created"`
    UpdateTime  models.Time `xorm:"updated"`
    DeleteTime  models.Time `xorm:"deleted"`
}

type ServerBinding struct {
    Id    string
    Count int
}

func (p PortBinding) TableName() string {
    return "fcs_port_binding"
}

// 找到绑定网卡最少的虚机
func GetAvailableServer() (*ServerBinding, error) {
    sql := "SELECT ps.id id, IFNULL(pb.count,0) count from fcs_proxy_server ps " +
        "LEFT JOIN (select server_id, count(*) count FROM fcs_port_binding WHERE delete_time is NULL GROUP BY server_id) as pb " +
        "on ps.id = pb.server_id WHERE ps.delete_time is NULL ORDER BY count DESC limit 1"
    bindingResult := make([]*ServerBinding, 0)
    err := models.Engine.SQL(sql).Find(&bindingResult)
    if err != nil {
        _ = logger.Error("GetAvailableServer: get fcs_proxy_server error, %v", err)
        return nil, err
    }
    if len(bindingResult) == 0 {
        _ = logger.Warn("GetFuncInfoByGroup: did not find any data")
        return nil, errors.New("did not find any data")
    }
    return bindingResult[0], nil
}

func CreatePortBinding(binding *PortBinding) (*PortBinding, error) {
    if _, err := models.Engine.Insert(binding); err != nil {
        _ = logger.Error("CreatePortBinding: insert error: %v", err)
        return nil, err
    }
    return binding, nil
}

func FindPortBinding(vpcId string) (bool, *PortBinding, error) {
    portBinding := new(PortBinding)
    has, err := models.Engine.Where("vpc_id=?", vpcId).Get(portBinding)
    if err != nil {
        _ = logger.Error("FindPortBinding: find binding error, %s", err.Error())
        return false, nil, err
    }
    return has, portBinding, nil
}

func FindPortBindingBySubnet(subnetId string) (bool, *PortBinding, error) {
    portBinding := new(PortBinding)
    has, err := models.Engine.Where("subnet_id=?", subnetId).Get(portBinding)
    if err != nil {
        _ = logger.Error("FindPortBindingBySubnet: find binding error, %s", err.Error())
        return false, nil, err
    }
    return has, portBinding, nil
}

func DeletePortBinding(vpcId string) (int, error) {
    // 使用vpcId将所有的记录删除（主备的两台机器的话可能有多条记录，但是目前还没有实现主备策略）
    // 防止并发操作有绑定操作，这里使用乐观锁来防止这种情况
    line, err := models.Engine.Where("vpc_id=?", vpcId).And("function_num<=1").Delete(new(PortBinding))
    if err != nil {
        _ = logger.Error("DeletePortBinding: delete binding error: %s", err.Error())
        return 0, err
    }
    return int(line), err
}

func CutPortBindingNumber(vpcId string) (int, error) {
    sql := "update fcs_port_binding set function_num=function_num-1 where vpc_id=? and delete_time is null"
    res, err := models.Engine.Exec(sql, vpcId)
    if err != nil {
        _ = logger.Error("CutPortBindingNumber: updating binding error: %s", err.Error())
        return 0, err
    }
    line, err := res.RowsAffected()
    return int(line), err
}

func AddPortBindingNumber(vpcId string) (int, error) {
    sql := "update fcs_port_binding set function_num=function_num+1 where vpc_id=? and delete_time is null"
    res, err := models.Engine.Exec(sql, vpcId)
    if err != nil {
        _ = logger.Error("AddPortBindingNumber: updating binding error: %s", err.Error())
        return 0, err
    }
    line, err := res.RowsAffected()
    return int(line), err
}
